/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package avl.sv.server.sources;

import avl.sv.server.PermissionsSet;
import avl.sv.shared.Permissions;
import avl.sv.shared.XML.SolutionXML_Parser;
import avl.sv.shared.sources.SolutionSource;
import avl.sv.shared.Solution;
import avl.sv.shared.XML.SolutionXML_Writer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.SortedSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
import oracle.kv.Depth;
import oracle.kv.KVStore;
import oracle.kv.Key;
import oracle.kv.Value;
import oracle.kv.ValueVersion;
import org.xml.sax.SAXException;

/**
 *
 * @author benbryan
 */
public class SolutionSourceKVStore extends SolutionSource {
    KVStore kvstore;
    
    private SolutionSourceKVStore(KVStore kvstore, String solutionName, int id) {
        super(solutionName, id);
        this.kvstore = kvstore;
    }
    
    public long getVersion(){
        ArrayList<String> major = new ArrayList<>();
        major.add("solution");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(String.valueOf(getId()));
        ValueVersion vv = kvstore.get(Key.createKey(major));
        if (vv == null){
            kvstore.put(Key.createKey(major), Value.EMPTY_VALUE);
            vv = kvstore.get(Key.createKey(major));
        }
        return vv.getVersion().getVersion();
    }
    
    public static SolutionSourceKVStore get(KVStore kvstore, int id){
        ArrayList<String> major = new ArrayList<>();
        major.add("solution");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(String.valueOf(id));
        ValueVersion temp = kvstore.get(Key.createKey(major));
        if (temp == null){
            return null;
        } else {
            ArrayList<String> minor = new ArrayList<>();
            minor.add("name");
            temp = kvstore.get(Key.createKey(major, minor));
            String solutionName;
            if (temp != null){
                solutionName = new String(temp.getValue().getValue());
            } else {
                solutionName = "failed to get solution name";
            }
            return new SolutionSourceKVStore(kvstore, solutionName, id);
        }
    }
    
    public static SolutionSourceKVStore create(KVStore kvstore, String userName, String solutionName){      
        int id = createID(kvstore, solutionName);
        SolutionSourceKVStore ss = new SolutionSourceKVStore(kvstore, solutionName, id);
        ss.setPermission(userName, Permissions.ADMIN); 
        return ss;
    }

    private static int createID(KVStore kvstore, String solutionName){
        ArrayList<String> major = new ArrayList<>();
        major.add("solution");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(""); // this gets replaced below
        while (true){
            //TODO: this could cause things to hang, maybe. 
            int r = (int) (Math.random()*Math.pow(2, 32));
            major.set(1, String.valueOf(r));
            ValueVersion vv = kvstore.get(Key.createKey(major));
            if (vv == null){
                kvstore.put(Key.createKey(major), Value.EMPTY_VALUE);
                ArrayList<String> minor = new ArrayList<>();
                minor.add("name");
                kvstore.put(Key.createKey(major, minor),Value.createValue(solutionName.getBytes()));
                return r;
            }
        }
    }
        
    public Permissions getPermissions(String userName){
        ArrayList<PermissionsSet> pSets = getPermissionsSets(kvstore, userName);
        for (PermissionsSet pSet:pSets){
            if (pSet.getID() == getId()){
                return pSet.getPermission();
            }
        }
        return Permissions.DENIED;
    }
    public void setPermission( final String userName, final Permissions permission) {
        PermissionsSet newSet = new PermissionsSet(permission, getId());
        ArrayList<PermissionsSet> permissionSets = getPermissionsSets(kvstore, userName);
        boolean entryUpdated = false;
        for (int i = 0; i < permissionSets.size(); i++){
            if (permissionSets.get(i).getID() == getId()){
                if (permission == Permissions.DENIED){
                    permissionSets.remove(i);
                } else {
                    permissionSets.set(i, newSet);
                }
                entryUpdated = true;
                break;
            }
        }
        if (entryUpdated == false){
            permissionSets.add(newSet);                
        }
        setPermissionsSets( kvstore, userName, permissionSets);
        updateUserList(userName, permission);
    }
            
    public static ArrayList<PermissionsSet> getPermissionsSets(KVStore kvstore, final String userName) {
        ArrayList<String> major = new ArrayList<>();
        major.add("user");
        major.add(userName);
        ArrayList<String> minor = new ArrayList<>();
        minor.add("permissions");
        minor.add("solution");
        ArrayList<PermissionsSet> permissionSet = new ArrayList<>();
        ValueVersion vv = kvstore.get(Key.createKey(major, minor));
        if ((vv == null) || (vv.getValue() == null)){
            return permissionSet;
        }
        String entries[] = new String(vv.getValue().getValue()).split(System.lineSeparator());
        for (String entry:entries){
            // entry should be in the format   name:permission:ID
            String[] str = entry.split(",");
            Permissions p;
            if (str.length != 2){
                continue;
            }
            try{
                p = Permissions.valueOf(str[0]);
            } catch (Exception ex){
                continue;
            } 
            permissionSet.add(new PermissionsSet(p, Integer.valueOf(str[1])));
        }
        return permissionSet;
    }  
    private static void setPermissionsSets(final KVStore kvstore, final String userName, final ArrayList<PermissionsSet> permissionSets) {
        ArrayList<String> major = new ArrayList<>();
        major.add("user");
        major.add(userName);
        ArrayList<String> minor = new ArrayList<>();
        minor.add("permissions");
        minor.add("solution");
        StringBuilder sb = new StringBuilder();
        for (PermissionsSet permissionSet:permissionSets){
            // entry should be in the format   name:permission:ID
            String entry = permissionSet.getPermission().name() + "," + permissionSet.getID() + System.lineSeparator();
            sb.append(entry);
        }
        kvstore.put(Key.createKey(major,minor), Value.createValue(sb.toString().getBytes()));
    }  
    public static long getPermissionsSetsVersion(KVStore kvstore, final String userName) {
        ArrayList<String> major = new ArrayList<>();
        major.add("user");
        major.add(userName);
        ArrayList<String> minor = new ArrayList<>();
        minor.add("permissions");
        minor.add("solution");
        ValueVersion vv = kvstore.get(Key.createKey(major, minor));
        if ((vv == null) || (vv.getValue() == null)){
            Value v = Value.createValue("".getBytes());
            kvstore.put(Key.createKey(major, minor), v);
            vv = kvstore.get(Key.createKey(major, minor));
        } 
        return vv.getVersion().getVersion();
    }  
    
    public String getUserList(){   
        ArrayList<String> major = new ArrayList<>();
        major.add("solution");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(String.valueOf(getId()));
        ArrayList<String> minor = new ArrayList<>();
        minor.add("users");     
        ValueVersion vv = kvstore.get(Key.createKey(major, minor));
        if (vv == null){
            return "";
        } else {
            return new String(vv.getValue().getValue());
        }
    }
    private void updateUserList(String userName, Permissions permission){   
        HashSet<String> users = new HashSet<>();
        users.addAll(Arrays.asList(getUserList().split(";")));
        users.add(userName);
        if (permission.equals(Permissions.DENIED)){
            users.remove(userName);
        }
        StringBuilder sb = new StringBuilder();
        for (String user:users){
            if (user.isEmpty()){
                continue;
            }
            sb.append(user).append(";");
        }
        setUserList(sb.toString());
    }
    private void setUserList(String userList){
        ArrayList<String> major = new ArrayList<>();
        major.add("solution");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(String.valueOf(getId()));
        ArrayList<String> minor = new ArrayList<>();
        minor.add("users");     
        kvstore.put(Key.createKey(major, minor), Value.createValue(userList.getBytes()));        
    }
    
    @Override
    public String setSolution(String xml) {
        ArrayList<String> major = new ArrayList<>();
        major.add("solution");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(String.valueOf(getId()));
        kvstore.put(Key.createKey(major), Value.createValue(xml.getBytes()));
        return "success";
    }

    public String getSolutionXML() {
        ArrayList<String> major = new ArrayList<>();
        major.add("solution");
        kvstore.putIfAbsent(Key.createKey(major), Value.EMPTY_VALUE);
        major.add(String.valueOf(getId()));
        ValueVersion value = kvstore.get(Key.createKey(major));
        if ((value == null) || (value.getValue() == null)) {
            Solution solution = new Solution(getName());
            return SolutionXML_Writer.getXMLString(solution);
        }
        String xml = new String(value.getValue().getValue());
        return xml;
    }

    @Override
    public Solution getSolution() {
        try {
            String xml = getSolutionXML();
            if (xml == null) {
                return null;
            }
            SolutionXML_Parser parser = new SolutionXML_Parser();
            if (xml.equals("")) {
                return null;
            }
            Solution solution = parser.parse(xml);
            return solution;
        } catch (ParserConfigurationException | SAXException | IOException ex) {
            Logger.getLogger(SolutionSourceKVStore.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    @Override
    public String delete() {
        if (getUserList().length() < 2){
            ArrayList<String> major = new ArrayList<>();
            major.add("solution");
            kvstore.put(Key.createKey(major), Value.EMPTY_VALUE);
            major.add(String.valueOf(getId()));
            SortedSet<Key> keys = kvstore.multiGetKeys(Key.createKey(major), null, Depth.PARENT_AND_DESCENDANTS);
            for (Key k:keys){
                kvstore.delete(k);
            }
            return "Deleted";
        }
        return "Not Deleted";
    }

}
